/**
 * Universidad del Valle de Guatemala
 * Algoritmos y Estructura de Datos
 * Sección 10
 * Carlos Alberto Solórzano Pineda (08832)
 * 06 de septiembre de 2011
 * @author carlos
 * @version 1.2
 *
 * Clase principal que emula un banco
 */

// Paquetes a utilizar
import java.util.*;

public class MainBank {

	/**
	 * Ejecución del programa principal
	 * @param args
	 */
	public static void main(String[] args) {
		// Variables locales a utilizar
		int hour = 0;					// Hora de llegada del cliente
		int queueNum = 0;				// Cola en la que se atenderá al cliente
		Event tmp;						// Evento temporal del cliente
		int totalTime = 0;				// Tiempo total por todos los clientes 
		int numClient = 0;				// Número total de clientes
		Random random = new Random();	// Random para tiempo de transacciones
		
		// Inicialización de objetos Cola y Lista
		@SuppressWarnings("unchecked")
		CircularQueue<Client>[] q = new CircularQueue[4];
		for (int i = 0; i < 4; i++){
			q[i] = new CircularQueue<Client>();
		}
		
		LinkedList<Event> list = new LinkedList<Event>();
		
		printMenu(0);
		
		do{
			printMenu(1);
			hour = getNumber();
			// Verificar que la lista no esté vacía
			if (list.size() != 0){
				// Verificar que se ingresen ordenados los números, y que
				// la hora de entrada del banco no sea la hora del cierre,
				// es decir, 600 minutos después de abierto
				if(hour > list.getLast().getHour() && hour < 600){
					list.add(new Event(hour, false, 0));
				}
				else{
					if(hour <= 0){
						break;
					}else{
						printMenu(2);
					}
				}
			}
			else{
				// Si es el primer cliente, verifica que no sea la hora
				// de cierre del banco
				if(hour != 600){
					list.add(new Event(hour, false, 0));
				}else{
					printMenu(3);
				}
			}
		}while(hour > 0);
		
		// Lista de eventos
		do{
			// Almacenar primer evento en temporal
			tmp = list.removeFirst();
			if(!tmp.isExit()){	
			// Evento de entrada
				queueNum = getSmaller(q);
				try{
					// Tiempo de transacción
					int t = random.nextInt(30) + 1;
					// Añadir un nuevo cliente
					q[queueNum].add(new Client(tmp.getHour(), t));
					// Tiempo total de operaciones
					totalTime = totalTime + t;
					// Cantidad total de clientes
					numClient++;
					// Añadir un evento a la lista de eventos
					for(int i = 0; i < list.size();){
						Client c = q[queueNum].peek();
						if(c.getDepartureTime() >= list.get(i).getHour()){
							list.add(i--, new Event(c.getDepartureTime(), true, 1));
						}
						break;
					}
				}catch(Exception e){
					printMenu(666);
				}
			}else{
			// Evento de salida
				// Elimina un cliente de la cola específica
				q[list.peek().getQueue()].remove();
				// Eliminar evento de la lista
				list.remove();
			}
		}while(!list.isEmpty());
		
		// Imprimir resultados
		System.out.println("El tiempo promedio por cliente es de "  
			+ (totalTime / numClient) + " " + 
			"minutos por cliente..."
				);
	}
	
	/**
	 * 
	 * @param q El arreglo de colas circulares tipo Cliente para comparar
	 * @return El número de la cola más corta
	 */
	private static int getSmaller(CircularQueue<Client>[] q){
		int ret = 0;
		int c1 = q[0].size();
		int c2 = q[1].size();
		int c3 = q[2].size();
		int c4 = q[3].size();
		
		// Comparar qué cola es menor
		// Cola 1 es menor
		if ((c1 <= c2) && (c1 <= c3) && (c1 <= c4)){
			ret = 1;
		}
		
		// Cola 2 es menor
		if ((c2 < c1) && (c2 <= c3) && (c2 <= c4)){
			ret = 2;
		}
		
		// Cola 3 es menor
		if ((c3 < c1) && (c3 < 2) && (c3 <= c4)){
			ret = 3;
		}
		
		// Cola 4 es menor
		if ((c4 < c1) && (c4 < c2) && (c4 < c3)){
			ret = 4;
		}
		
		return ret;
	}
	
	/**
	 * Imprime el menú indicado en pantalla
	 * @param n El número de menú a imprimir
	 */
	private static void printMenu(int n){
		switch(n){
		case 0:
			System.out.println("++++++++----------------------++++++++");
			System.out.println("        ++++--------------++++");
			System.out.println("        Bienvenido a Banco UVG");
			System.out.println("        ++++--------------++++");
			System.out.println("++++++++----------------------++++++++");
			break;
		case 1:
			System.out.println("Ingreso de tiempos...");
			System.out.println("Los tiempos deben ser números enteros positivos...");
			System.out.println("Si ingresa un número negativo o cero, terminará de ingresar datos...");
			System.out.println("Ingrese el tiempo de llegada de los clientes: ");
			break;
		case 2:
			System.out.println("Usted no puede retroceder en el tiempo...");
			System.out.println("Ingrese un tiempo de llegada mayor al anterior...");		
			break;
		case 3:
			System.out.println("El banco está cerrado...");
			System.out.println("Por favor, regrese otro día...");
			break;
		case 666:
			System.out.println("Disculpe las molestias, el sistema ha tenido un error...");
			break;
		case 777:
			System.out.println("Por favor, ingrese sólo números...");
			break;
		}
	}
	
	/**
	 * 
	 * @return El número ingresado por teclado
	 */
	private static int getNumber(){
		Scanner scan = new Scanner(System.in);
		int ret = 0;
		try{
			ret = scan.nextInt();
		}catch(InputMismatchException except){
			printMenu(777);
		}finally{
			scan.nextLine();
		}
		return ret;
	}
}
